package com.ittao.taoaicodecommon.manage.storage.service.impl;

import com.ittao.taoaicodecommon.config.CosClientConfig;
import com.ittao.taoaicodecommon.manage.storage.callback.DownloadProgressCallback;
import com.ittao.taoaicodecommon.manage.storage.entity.DownloadStatistics;
import com.ittao.taoaicodecommon.manage.storage.entity.FileClassification;
import com.ittao.taoaicodecommon.manage.storage.entity.PerformanceMetrics;
import com.ittao.taoaicodecommon.manage.storage.lifecycle.tracker.SessionActivityTracker;
import com.ittao.taoaicodecommon.manage.storage.service.CosOperationService;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * COS操作服务实现类
 * 提供COS文件和目录操作的核心功能
 */
@Slf4j
@Service
@ConditionalOnBean(COSClient.class)
public class CosOperationServiceImpl implements CosOperationService {

    @Resource
    private COSClient cosClient;

    @Resource
    private CosClientConfig cosClientConfig;

    @Resource
    private SessionActivityTracker sessionActivityTracker;

    /**
     * 获取COS目录下的所有文件列表
     */
    private List<COSObjectSummary> getAllFilesFromCos(COSClient cosClient, String bucketName, String cosDir) {
        List<COSObjectSummary> allFiles = new ArrayList<>();

        try {
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
            listObjectsRequest.setBucketName(bucketName);
            listObjectsRequest.setPrefix(cosDir);
            listObjectsRequest.setMaxKeys(1000); // 每次最多获取1000个对象

            ObjectListing objectListing;
            do {
                objectListing = cosClient.listObjects(listObjectsRequest);
                List<COSObjectSummary> objectSummaries = objectListing.getObjectSummaries();

                // 过滤掉目录本身（以/结尾的对象）
                objectSummaries.stream()
                        .filter(obj -> !obj.getKey().endsWith("/"))
                        .forEach(allFiles::add);

                listObjectsRequest.setMarker(objectListing.getNextMarker());
            } while (objectListing.isTruncated());

            log.debug("从COS目录 {} 获取到 {} 个文件", cosDir, allFiles.size());

        } catch (Exception e) {
            log.error("获取COS文件列表失败: {}", e.getMessage(), e);
            throw new RuntimeException("获取COS文件列表失败: " + e.getMessage(), e);
        }

        return allFiles;
    }

    /**
     * 根据文件大小分类文件
     */
    private FileClassification classifyFiles(List<COSObjectSummary> files) {
        FileClassification classification = new FileClassification();

        for (COSObjectSummary file : files) {
            long size = file.getSize();
            if (size < 100 * 1024) { // < 100KB
                classification.getSmallFiles().add(file);
            } else if (size < 10 * 1024 * 1024) { // < 10MB
                classification.getMediumFiles().add(file);
            } else { // >= 10MB
                classification.getLargeFiles().add(file);
            }
        }

        return classification;
    }

    /**
     * 根据文件分类计算最优线程池大小
     */
    private int calculateOptimalThreadPoolSize(FileClassification classification) {
        int smallFileCount = classification.getSmallFiles().size();
        int mediumFileCount = classification.getMediumFiles().size();
        int largeFileCount = classification.getLargeFiles().size();

        // 基于文件类型和数量动态计算线程池大小
        int threadPoolSize = Math.min(
                Math.max(4, smallFileCount / 10 + mediumFileCount / 5 + largeFileCount),
                20 // 最大20个线程
        );

        log.debug("计算线程池大小: 小文件={}, 中等文件={}, 大文件={}, 线程池大小={}",
                smallFileCount, mediumFileCount, largeFileCount, threadPoolSize);

        return threadPoolSize;
    }

    /**
     * 并行下载文件列表
     */
    private List<CompletableFuture<Void>> downloadFilesInParallel(
            List<COSObjectSummary> files,
            Path localDirPath,
            String normalizedCosDir,
            ExecutorService executorService,
            DownloadStatistics stats,
            DownloadProgressCallback progressCallback,
            String fileType) {

        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (COSObjectSummary file : files) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() ->
                    downloadSingleFileWithRetry(file, localDirPath, normalizedCosDir, stats, progressCallback), executorService);
            futures.add(future);
        }

        log.debug("启动{}并行下载，文件数量: {}", fileType, files.size());
        return futures;
    }

    /**
     * 下载单个文件（带重试机制）
     */
    private void downloadSingleFileWithRetry(
            COSObjectSummary file,
            Path localDirPath,
            String normalizedCosDir,
            DownloadStatistics stats,
            DownloadProgressCallback progressCallback) {

        String cosObjectKey = file.getKey();
        String relativePath = cosObjectKey.substring(normalizedCosDir.length());
        Path localFilePath = localDirPath.resolve(relativePath);

        int maxRetries = 3;
        int retryCount = 0;
        boolean downloadSuccess = false;

        while (!downloadSuccess) {
            try {
                // 确保文件的父目录存在
                Path parentDir = localFilePath.getParent();
                if (parentDir != null && !Files.exists(parentDir)) {
                    Files.createDirectories(parentDir);
                }

                // 下载文件
                GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), cosObjectKey);
                COSObject cosObject = cosClient.getObject(getObjectRequest);

                try (InputStream inputStream = cosObject.getObjectContent();
                     FileOutputStream outputStream = new FileOutputStream(localFilePath.toFile())) {

                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    long downloadedBytes = 0;

                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                        downloadedBytes += bytesRead;
                    }

                    // 更新统计信息
                    stats.getCompletedFiles().incrementAndGet();
                    stats.getDownloadedSize().addAndGet(downloadedBytes);

                    if (progressCallback != null) {
                        progressCallback.onFileCompleted(relativePath, downloadedBytes);
                        progressCallback.onProgress(
                                stats.getCompletedFiles().get(),
                                stats.getDownloadedSize().get(),
                                stats.getTotalFiles(),
                                stats.getTotalSize()
                        );
                    }

                    downloadSuccess = true;
                    log.debug("文件下载成功: {} -> {}", cosObjectKey, localFilePath);

                } catch (IOException e) {
                    log.warn("文件写入失败 (重试 {}/{}): {} -> {}, 错误: {}",
                            retryCount + 1, maxRetries, cosObjectKey, localFilePath, e.getMessage());
                    retryCount++;
                    if (retryCount >= maxRetries) {
                        stats.getFailedFiles().incrementAndGet();
                        if (progressCallback != null) {
                            progressCallback.onFileError(relativePath, e);
                        }
                        throw new RuntimeException("文件下载失败，已达到最大重试次数: " + cosObjectKey, e);
                    }
                }

            } catch (CosClientException e) {
                log.warn("COS异常 (重试 {}/{}): {} -> {}, 错误: {}",
                        retryCount + 1, maxRetries, cosObjectKey, localFilePath, e.getMessage());
                retryCount++;
                if (retryCount >= maxRetries) {
                    stats.getFailedFiles().incrementAndGet();
                    if (progressCallback != null) {
                        progressCallback.onFileError(relativePath, e);
                    }
                    throw new RuntimeException("文件下载失败，已达到最大重试次数: " + cosObjectKey, e);
                }
            } catch (IOException e) {
                log.error("创建目录失败: {}, 错误: {}", localFilePath.getParent(), e.getMessage());
                stats.getFailedFiles().incrementAndGet();
                if (progressCallback != null) {
                    progressCallback.onFileError(relativePath, e);
                }
                throw new RuntimeException("创建目录失败: " + e.getMessage(), e);
            }
        }
    }

    @Override
    public void downloadDirectoryFromCosConcurrent(String cosDir, String localDir, DownloadProgressCallback progressCallback) {
        String bucketName = cosClientConfig.getBucket();

        if (cosClient == null || bucketName == null) {
            log.error("COS客户端或配置未初始化，无法执行下载操作");
            throw new IllegalStateException("COS客户端或配置未初始化");
        }

        ExecutorService executorService = null;

        try {
            long startTime = System.currentTimeMillis();
            log.info("开始并发下载目录: {} -> {}", cosDir, localDir);

            // 1. 确保本地根目录存在
            Path localDirPath = Paths.get(localDir);
            Files.createDirectories(localDirPath);

            // 2. 确保COS路径以/结尾
            String normalizedCosDir = cosDir.endsWith("/") ? cosDir : cosDir + "/";

            // 3. 获取所有文件列表
            List<COSObjectSummary> allFiles = getAllFilesFromCos(cosClient, bucketName, normalizedCosDir);

            if (allFiles.isEmpty()) {
                log.info("COS目录为空，无需下载: {}", cosDir);
                if (progressCallback != null) {
                    progressCallback.onComplete(0, 0, 0);
                }
                return;
            }

            // 4. 文件分类和统计
            FileClassification classification = classifyFiles(allFiles);
            long totalSize = allFiles.stream().mapToLong(COSObjectSummary::getSize).sum();
            DownloadStatistics stats = new DownloadStatistics(allFiles.size(), totalSize);

            log.info("文件分类统计 - 小文件: {}, 中等文件: {}, 大文件: {}, 总大小: {} MB",
                    classification.getSmallFiles().size(),
                    classification.getMediumFiles().size(),
                    classification.getLargeFiles().size(),
                    totalSize / (1024 * 1024));

            if (progressCallback != null) {
                progressCallback.onStart(allFiles.size(), totalSize);
            }

            // 5. 创建线程池并开始下载
            int threadPoolSize = calculateOptimalThreadPoolSize(classification);
            executorService = Executors.newFixedThreadPool(threadPoolSize);

            List<CompletableFuture<Void>> futures = new ArrayList<>();

            // 小文件：高并发下载
            if (!classification.getSmallFiles().isEmpty()) {
                futures.addAll(downloadFilesInParallel(classification.getSmallFiles(), localDirPath,
                        normalizedCosDir, executorService, stats, progressCallback, "小文件"));
            }

            // 中等文件：适中并发下载
            if (!classification.getMediumFiles().isEmpty()) {
                futures.addAll(downloadFilesInParallel(classification.getMediumFiles(), localDirPath,
                        normalizedCosDir, executorService, stats, progressCallback, "中等文件"));
            }

            // 大文件：低并发下载
            if (!classification.getLargeFiles().isEmpty()) {
                futures.addAll(downloadFilesInParallel(classification.getLargeFiles(), localDirPath,
                        normalizedCosDir, executorService, stats, progressCallback, "大文件"));
            }

            // 6. 等待所有下载完成
            CompletableFuture<Void> allDownloads = CompletableFuture.allOf(
                    futures.toArray(new CompletableFuture[0]));

            allDownloads.get(300, TimeUnit.SECONDS); // 最多等待5分钟

            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            log.info("并发下载目录完成: {} -> {}, 共下载 {} 个文件, 总大小: {} MB, 耗时: {} ms, 平均速度: {} MB/s",
                    cosDir, localDir, stats.getCompletedFiles().get(),
                    stats.getTotalSize() / (1024 * 1024),
                    duration,
                    String.format("%.2f", (stats.getTotalSize() / (1024.0 * 1024.0)) / (duration / 1000.0)));

            if (progressCallback != null) {
                progressCallback.onComplete(stats.getCompletedFiles().get(), stats.getDownloadedSize().get(), duration);
            }

            // 7. 下载完成后，自动启动清理机制
            if (sessionActivityTracker != null) {
                triggerAutomaticCleanup(localDir, sessionActivityTracker);
            }

        } catch (Exception e) {
            log.error("并发下载目录失败: {} -> {}, 错误信息: {}", cosDir, localDir, e.getMessage(), e);
            if (progressCallback != null) {
                progressCallback.onError(e);
            }
            throw new RuntimeException("并发下载目录失败: " + e.getMessage(), e);
        } finally {
            // 关闭线程池
            if (executorService != null) {
                executorService.shutdown();
                try {
                    if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
                        executorService.shutdownNow();
                    }
                } catch (InterruptedException e) {
                    executorService.shutdownNow();
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    @Override
    public boolean uploadBuildResults(String localDir, String cosDir) {
        try {
            log.info("开始上传构建结果: {} -> {}", localDir, cosDir);

            Path localDirPath = Paths.get(localDir);
            if (!Files.exists(localDirPath) || !Files.isDirectory(localDirPath)) {
                log.error("本地目录不存在或不是目录: {}", localDir);
                return false;
            }

            // 递归上传目录
            int uploadedCount = uploadDirectoryRecursively(cosClient, cosClientConfig.getBucket(),
                    localDirPath, cosDir, "");

            log.info("构建结果上传完成: {} -> {}, 共上传 {} 个文件", localDir, cosDir, uploadedCount);
            return uploadedCount > 0;

        } catch (Exception e) {
            log.error("上传构建结果失败: {} -> {}, 错误信息: {}", localDir, cosDir, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 递归上传目录
     */
    private int uploadDirectoryRecursively(COSClient cosClient, String bucketName,
                                          Path localDir, String cosBaseDir, String relativePath) throws IOException {
        int uploadCount = 0;

        try (var stream = Files.list(localDir)) {
            for (Path path : stream.toList()) {
                String fileName = path.getFileName().toString();
                String newRelativePath = relativePath.isEmpty() ? fileName : relativePath + "/" + fileName;

                if (Files.isDirectory(path)) {
                    // 递归处理子目录
                    uploadCount += uploadDirectoryRecursively(cosClient, bucketName, path, cosBaseDir, newRelativePath);
                } else {
                    // 上传文件
                    String cosKey = cosBaseDir + "/" + newRelativePath;
                    cosKey = cosKey.replaceAll("/+", "/"); // 清理多余的斜杠

                    PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, cosKey, path.toFile());
                    cosClient.putObject(putObjectRequest);

                    uploadCount++;
                    log.debug("文件上传成功: {} -> {}", path, cosKey);
                }
            }
        }

        return uploadCount;
    }

    @Override
    public boolean deleteDirectory(String cosDir) {
        try {
            log.info("开始删除COS目录: {}", cosDir);

            String bucketName = cosClientConfig.getBucket();
            String normalizedCosDir = cosDir.endsWith("/") ? cosDir : cosDir + "/";

            // 列出目录下的所有对象
            List<COSObjectSummary> allObjects = new ArrayList<>();
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
            listObjectsRequest.setBucketName(bucketName);
            listObjectsRequest.setPrefix(normalizedCosDir);
            listObjectsRequest.setMaxKeys(1000);

            ObjectListing objectListing;
            do {
                objectListing = cosClient.listObjects(listObjectsRequest);
                allObjects.addAll(objectListing.getObjectSummaries());
                listObjectsRequest.setMarker(objectListing.getNextMarker());
            } while (objectListing.isTruncated());

            if (allObjects.isEmpty()) {
                log.info("COS目录为空，无需删除: {}", cosDir);
                return true;
            }

            // 批量删除对象
            int deletedCount = batchDeleteObjects(allObjects);

            log.info("COS目录删除完成: {}, 共删除 {} 个对象", cosDir, deletedCount);
            return deletedCount == allObjects.size();

        } catch (Exception e) {
            log.error("删除COS目录失败: {}, 错误信息: {}", cosDir, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 批量删除对象
     */
    private int batchDeleteObjects(List<COSObjectSummary> objectSummaries) {
        int deletedCount = 0;
        String bucketName = cosClientConfig.getBucket();

        // 分批删除，每批最多1000个对象
        int batchSize = 1000;
        for (int i = 0; i < objectSummaries.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, objectSummaries.size());
            List<COSObjectSummary> batch = objectSummaries.subList(i, endIndex);

            try {
                DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName);
                List<DeleteObjectsRequest.KeyVersion> keys = new ArrayList<>();

                for (COSObjectSummary obj : batch) {
                    keys.add(new DeleteObjectsRequest.KeyVersion(obj.getKey()));
                }

                deleteObjectsRequest.setKeys(keys);
                DeleteObjectsResult deleteResult = cosClient.deleteObjects(deleteObjectsRequest);

                // 检查是否有删除失败的对象
                if (deleteResult.getDeletedObjects() != null && !deleteResult.getDeletedObjects().isEmpty()) {
                    deletedCount += deleteResult.getDeletedObjects().size();
                    log.debug("批量删除对象成功，本批删除 {} 个对象", deleteResult.getDeletedObjects().size());
                }

            } catch (Exception e) {
                log.error("批量删除对象失败，批次范围: {} - {}, 错误信息: {}",
                        i, endIndex - 1, e.getMessage(), e);
            }
        }

        return deletedCount;
    }

    @Override
    public boolean fileExists(String cosPath) {
        try {
            String bucketName = cosClientConfig.getBucket();
            
            // 使用getObjectMetadata方法检查文件是否存在
            GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest(bucketName, cosPath);
            cosClient.getObjectMetadata(getObjectMetadataRequest);
            
            log.debug("文件存在检查: {} - 存在", cosPath);
            return true;
            
        } catch (CosServiceException e) {
            if (e.getStatusCode() == 404) {
                log.debug("文件存在检查: {} - 不存在", cosPath);
                return false;
            } else {
                log.error("检查文件是否存在时发生COS服务异常: {}, 错误码: {}, 错误信息: {}",
                        cosPath, e.getErrorCode(), e.getErrorMessage());
                throw new RuntimeException("检查文件是否存在失败: " + e.getErrorMessage(), e);
            }
        } catch (CosClientException e) {
            log.error("检查文件是否存在时发生COS客户端异常: {}, 错误信息: {}",
                    cosPath, e.getMessage());
            throw new RuntimeException("检查文件是否存在失败: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFile(String key) {
        try {
            String bucketName = cosClientConfig.getBucket();
            
            DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucketName, key);
            cosClient.deleteObject(deleteObjectRequest);
            
            log.debug("文件删除成功: {}", key);
            
        } catch (CosServiceException e) {
            log.error("删除文件时发生COS服务异常: {}, 错误码: {}, 错误信息: {}",
                    key, e.getErrorCode(), e.getErrorMessage());
            throw new RuntimeException("删除文件失败: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            log.error("删除文件时发生COS客户端异常: {}, 错误信息: {}",
                    key, e.getMessage());
            throw new RuntimeException("删除文件失败: " + e.getMessage(), e);
        }
    }

    /**
     * 触发自动清理机制（增强版本，包含性能监控）
     *
     * @param localDir 本地目录路径
     * @param sessionActivityTracker 会话活动跟踪器
     */
    private void triggerAutomaticCleanup(String localDir, SessionActivityTracker sessionActivityTracker) {
        try {
            log.info("触发自动清理机制，目录: {}", localDir);

            // 记录清理前的目录状态
            PerformanceMetrics cleanupMetrics = new PerformanceMetrics("directory_cleanup");
            cleanupMetrics.recordDirectoryStats(localDir);

            // 生成唯一的sessionId
            String sessionId = "auto_cleanup_" + System.currentTimeMillis() + "_" + Math.abs(localDir.hashCode());

            // 从路径中提取userId和appId（如果可能）
            String userId = extractUserIdFromPath(localDir);
            String appId = extractAppIdFromPath(localDir);

            // 发布项目生成开始事件（注册会话监控）
            sessionActivityTracker.publishProjectGenerationStart(sessionId, userId, appId, localDir);

            // 发布项目生成完成事件
            sessionActivityTracker.publishProjectGenerationComplete(sessionId, userId, appId, localDir);

            // 记录清理操作的性能指标
            cleanupMetrics.complete();
            logPerformanceMetrics(cleanupMetrics);

            log.info("已为下载目录启动自动清理机制: sessionId={}, localDir={}", sessionId, localDir);

        } catch (Exception e) {
            log.warn("启动自动清理机制失败，但不影响下载操作: localDir={}, 错误信息: {}", localDir, e.getMessage());
        }
    }

    /**
     * 从文件路径中提取用户ID
     */
    private String extractUserIdFromPath(String path) {
        try {
            // 路径格式通常为: /temp/user_{userId}/session_{sessionId}/app_{appId}/...
            String[] parts = path.replace("\\", "/").split("/");
            for (String part : parts) {
                if (part.startsWith("user_")) {
                    return part.substring(5); // 移除 "user_" 前缀
                }
            }
        } catch (Exception e) {
            log.debug("无法从路径中提取用户ID: {}, 错误: {}", path, e.getMessage());
        }
        return "unknown_user";
    }

    /**
     * 从文件路径中提取应用ID
     */
    private String extractAppIdFromPath(String path) {
        try {
            // 路径格式通常为: /temp/user_{userId}/session_{sessionId}/app_{appId}/...
            String[] parts = path.replace("\\", "/").split("/");
            for (String part : parts) {
                if (part.startsWith("app_")) {
                    return part.substring(4); // 移除 "app_" 前缀
                }
            }
        } catch (Exception e) {
            log.debug("无法从路径中提取应用ID: {}, 错误: {}", path, e.getMessage());
        }
        return "unknown_app";
    }

    /**
     * 记录性能指标日志
     */
    private void logPerformanceMetrics(PerformanceMetrics metrics) {
        log.info("=== 性能指标报告 ===");
        log.info("操作类型: {}", metrics.getOperationType());
        log.info("执行时长: {} ms", metrics.getDuration());
        log.info("文件数量: {}", metrics.getFileCount());
        log.info("总大小: {} MB", metrics.getTotalSize() / (1024 * 1024));
        
        if (metrics.getTotalSize() > 0) {
            log.info("吞吐量: {} MB/s", String.format("%.2f", metrics.getThroughputMBps()));
        }
        
        if (metrics.getIOTime() > 0) {
            log.info("IO时间: {} ms", metrics.getIOTime());
        }
        
        if (metrics.getErrorMessage() != null) {
            log.info("错误信息: {}", metrics.getErrorMessage());
        }
        
        log.info("==================");
    }
}